package Gr

import (
	"db2s/global"
	"fmt"
	"strconv"
	"strings"
)

func (cs TableColumn) intTypeRedefine(m global.TableMeta) global.ConvertColumnMeta {
	var (
		ct        global.ConvertColumnMeta
		unsigned  bool
		precision int
		err       error
	)
	if strings.Contains(strings.ToLower(m.DataType), "unsigned") {
		unsigned = true
	}
	ct.Symbol = unsigned
	if precision, err = cs.stringToInt(m.DataPrecision); err != nil {
		return global.ConvertColumnMeta{}
	}
	ct.FixedSwitch = true
	ct.GroupType = func(s1 string) string {
		if n := strings.Index(strings.ToLower(s1), "unsigned"); n != -1 {
			return strings.TrimSpace(s1[:n])
		} else {
			return s1
		}
	}(m.DataType)
	ct.DataLengthUnit = "byte"
	switch strings.ToUpper(m.ColumnType) {
	case "BIT":
		/*
			存储u空间：BIT(M) 可以存储 M 位的二进制数据, 其中 M 的取值范围是 1 到 64
			默认值：如果不指定位数 M, 默认为 BIT(1)。
			赋值和检索:
				以二进制字符串的形式赋值: b'01010101'
				以十进制数字赋值: 65(等同于二进制 b'1000001')
				检索时以二进制字符串形式返回
			比较和运算:
				BIT 类型支持位运算, 如 &、|、^、~、<<、>>
				可以用 =、<>、<、>等比较运算符进行比较
			索引：BIT 类型的列可以创建索引, 但仅支持相等比较条件的查询
		*/
		ct.Type = "bit"
		ct.Format = "b01"
		ct.ShowSize = precision
		switch {
		case precision >= 1 && precision <= 8:
			ct.DataLength = 1
		case precision >= 9 && precision <= 16:
			ct.DataLength = 2
		case precision >= 17 && precision <= 24:
			ct.DataLength = 3
		case precision >= 25 && precision <= 32:
			ct.DataLength = 4
		case precision >= 33 && precision <= 40:
			ct.DataLength = 5
		case precision >= 41 && precision <= 48:
			ct.DataLength = 6
		case precision >= 49 && precision <= 56:
			ct.DataLength = 7
		case precision >= 57 && precision <= 64:
			ct.DataLength = 8
		}
		ct.MaxSize = 64
	case "TINYINT":
		/*
			TINYINT(1 byte) : -128 to 127 或 0 to 255 无符号
		*/
		ct.Type = "int"
		ct.ShowSize = precision
		ct.Format = ">=-128 and <= 127"
		if unsigned {
			ct.Format = ">=0 and <= 255"
		}
		ct.MaxSize = precision
		ct.DataLength = 1
	case "SMALLINT":
		//SMALLINT(2 bytes) : -32768 to 32767 或 0 to 65535 无符号
		ct.Type = "int"
		ct.ShowSize = precision
		ct.Format = ">=-32768 and <= 32767"
		if unsigned {
			ct.Format = ">=0 and <= 65535"
		}
		ct.MaxSize = precision
		ct.DataLength = 2
	case "MEDIUMINT":
		//MEDIUMINT(3 bytes) : -8388608 to 8388607 或 0 to 16777215 无符号
		ct.Type = "int"
		ct.ShowSize = precision
		ct.Format = ">=-8388608 and <= 8388607"
		ct.MaxSize = precision
		if unsigned {
			ct.Format = ">=0 and <= 16777215"
		}
		ct.DataLength = 3
	case "INT":
		//INT(4 bytes) : -2147483648 to 2147483647 或 0 to 4294967295 无符号
		ct.Type = "int"
		ct.Format = ">=-2147483648 and <= 2147483647"
		ct.MaxSize = precision
		ct.ShowSize = precision
		if unsigned {
			ct.Format = ">=0 and <= 4294967295"
		}
		ct.DataLength = 4
	case "BIGINT":
		//BIGINT(8 bytes) : -9223372036854775808 to 9223372036854775807 或 0 to 18446744073709551615 无符号
		ct.Type = "int"
		ct.ShowSize = precision
		ct.MaxSize = precision
		ct.Format = ">=-9223372036854775808 and <= 9223372036854775807"
		if unsigned {
			ct.Format = ">=0 and <= 18446744073709551615"
		}
		ct.DataLength = 8
	}
	return ct
}
func (cs TableColumn) FloatTypeRedefine(m global.TableMeta) global.ConvertColumnMeta {
	var (
		ct               global.ConvertColumnMeta
		unsigned         bool
		precision, scale int
		err              error
	)
	if strings.Contains(strings.ToLower(m.DataType), "unsigned") {
		unsigned = true
	}
	if precision, err = cs.stringToInt(m.DataPrecision); err != nil {
		return global.ConvertColumnMeta{}
	}
	ct.DataLengthUnit = "byte"
	ct.Symbol = unsigned
	ct.FixedSwitch = true
	ct.GroupType = m.DataType
	switch strings.ToUpper(m.ColumnType) {
	case "FLOAT":
		/*
			存储空间:
				FLOAT 类型占用 4 个字节的存储空间。
				它可以表示的数值范围为 -3.402823466E+38 到 -1.175494E-38, 0, 和 1.175494E-38 到 3.402823466E+38。
			精度:
				FLOAT 类型是单精度浮点数,它的精度一般为 7 位左右。
				也就是说,FLOAT 类型能够精确表示的小数位数大约为 7 位。
			声明方式:
				可以使用 FLOAT(M,D) 的方式声明,其中 M 表示数字的总位数,D 表示小数部分的位数。
				FLOAT 类型最多可以表示 24 位数字,其中 23 位为小数位,1 位为整数位
				例如 FLOAT(10,2) 表示一个总共 10 位,小数部分 2 位的浮点数。
			舍入:
				如果插入的数值超出了 FLOAT 的存储范围,MySQL 会自动进行舍入处理。
				舍入规则遵循四舍五入原则。
			精度损失:
				由于 FLOAT 是单精度浮点数,在某些场合可能会出现精度损失的问题。对于需要更高精度的场景,建议使用 DOUBLE 或 DECIMAL 类型。
				FLOAT 使用 IEEE 754 标准的单精度浮点数格式进行存储。这种格式包含以下三个部分:
					符号位 (1 bit)
					指数位 (8 bits)
					尾数位 (23 bits)
					2^23大约能表示7~8位数字
				在这种存储格式中,最大值是由指数位和尾数位决定的,而符号位只决定数值的正负。因此,无论是有符号还是无符号的 FLOAT，它们都使用相同的指数位和尾数位来表示数值。这就导致了它们的最大值是相同的。
			唯一的区别在于:
				有符号 FLOAT 可以表示正负数,范围为 -3.402823466E+38 到 3.402823466E+38。
				无符号 FLOAT 只能表示非负数,范围为 0 到 3.402823466E+38。
			所以虽然最大值是一样的,但是有符号 FLOAT 可以表示更广泛的数值范围。
		*/
		switch {
		case m.DataScale == "0":
			ct.ShowSize = precision
			ct.Format = "(precision)"
		case m.DataScale == "":
			ct.ShowSize = precision
			ct.Format = "(precision)"
		case len(m.DataScale) > 0:
			ct.ShowSize = fmt.Sprintf("%v,%v", precision, scale)
			ct.Format = "(precision,scale)"
		}
		ct.MaxSize = 23
		ct.Type = "float"
		ct.DataLength = 4
	case "DOUBLE":
		/*
			存储空间:
				DOUBLE 类型占用 8 个字节的存储空间,比 FLOAT 类型多占用 4 个字节。
				DOUBLE 类型可以表示的数值范围为 -1.7976931348623157E+308 到 -2.2250738585072014E-308, 0, 和 2.2250738585072014E-308 到 1.7976931348623157E+308。
			精度:
				DOUBLE 类型是双精度浮点数,它的精度通常为 15-16 位。也就是说,DOUBLE 类型能够精确表示的小数位数大约为 15-16 位。
				DOUBLE 类型遵循 IEEE 754 浮点数标准进行存储。
				64 位 DOUBLE 类型的存储格式如下:
				1 位符号位
				11 位指数位
				52 位尾数位
				52 位尾数位理论上可以表示 2^52 约 4.5e+15 个不同的值,但这并不意味着可以表示 15 位以上的有效数字
			声明方式:
				可以使用 DOUBLE(M,D) 的方式声明,其中 M 表示数字的总位数,D 表示小数部分的位数。
				例如 DOUBLE(10,2) 表示一个总共 10 位,小数部分 2 位的双精度浮点数。
			舍入:
				如果插入的数值超出了 DOUBLE 的存储范围,MySQL 会自动进行舍入处理。
				舍入规则遵循四舍五入原则。
			精度损失:
				由于 DOUBLE 是双精度浮点数,在某些场合也可能会出现精度损失的问题,但情况会比 FLOAT 好一些。
				对于需要严格控制精度的场景,建议使用 DECIMAL 类型。
			有符号 DOUBLE 的范围:
				最小值: -1.7976931348623157E+308
				最大值: 1.7976931348623157E+308
			无符号 DOUBLE 的范围:
				最小值: 0
				最大值: 1.7976931348623157E+308
		*/

		switch {
		case m.DataScale == "0":
			ct.ShowSize = precision
			ct.Format = "(precision)"
		case m.DataScale == "":
			ct.ShowSize = precision
			ct.Format = "(precision)"
		case len(m.DataScale) > 0:
			if scale, err = cs.stringToInt(m.DataScale); err != nil {
				return global.ConvertColumnMeta{}
			}
			ct.ShowSize = fmt.Sprintf("%v,%v", precision, scale)
			ct.Format = "(precision,scale)"
		}
		ct.MaxSize = 54
		ct.Type = "double"
		ct.DataLength = 8
	}
	return ct
}
func (cs TableColumn) DecimalTypeRedefine(m global.TableMeta) global.ConvertColumnMeta {
	var (
		ct               global.ConvertColumnMeta
		unsigned         bool
		precision, scale int
		err              error
	)
	/*
		DECIMAL 是 MySQL 中用于存储精确小数的数据类型。与 FLOAT 和 DOUBLE 不同, DECIMAL 类型可以精确地表示小数,不会出现精度损失的问题。
		以下是 DECIMAL 类型的一些特点:
			声明方式:
				DECIMAL(M,D) 其中 M 表示数字的总位数(包括小数部分), D 表示小数部分的位数。
				例如 DECIMAL(10,2) 表示一个总共 10 位,小数部分 2 位的小数。
			存储空间:
				DECIMAL 类型的存储空间是可变的,取决于声明时的 M 和 D 值。
				每个 DECIMAL(M,D) 值占用 M+2 个字节的存储空间(整数部分最多可以有 M-D 位,每个整数位需要 1 个字节来存储,小数部分最多可以有 D 位,每个小数位需要 1 个字节来存储,为了表示正负数,需要额外使用 1 个字节来存储符号位)。
			精度:
				DECIMAL 类型可以精确地表示小数,没有像 FLOAT 和 DOUBLE 那样的精度损失问题。
				最大精度为 65 位数字,小数部分最多 30 位。
			取值范围:
				DECIMAL(M,D) 的取值范围为 -10^M+D to 10^M-D。
				例如 DECIMAL(10,2) 的取值范围为 -99999999.99 to 99999999.99。
			舍入规则:
				如果插入的数值超出了 DECIMAL 的存储范围,MySQL 会按照四舍五入的方式进行舍入。
			有符号和无符号:
				DECIMAL 类型可以是有符号或无符号,通过关键字 SIGNED 和 UNSIGNED 来指定
	*/
	if strings.Contains(strings.ToLower(m.DataType), "unsigned") {
		unsigned = true
	}
	if precision, err = cs.stringToInt(m.DataPrecision); err != nil {
		return global.ConvertColumnMeta{}
	}
	if scale, err = cs.stringToInt(m.DataScale); err != nil {
		return global.ConvertColumnMeta{}
	}
	ct.Type = "decimal"
	ct.DataLength = precision + 1
	ct.FixedSwitch = false
	ct.GroupType = m.DataType
	ct.DataLengthUnit = "byte"
	ct.Symbol = unsigned
	switch {
	case m.DataScale == "0":
		ct.ShowSize = precision
		ct.Format = "Precision"
	case len(m.DataScale) > 0:
		ct.ShowSize = fmt.Sprintf("%v,%v", precision, scale)
		ct.Format = "Precision,Scale"
	}
	return ct
}
func (cs TableColumn) timeTypeRedefine(m global.TableMeta) global.ConvertColumnMeta {
	var (
		ct        global.ConvertColumnMeta
		unsigned  bool
		precision int
		err       error
	)
	if strings.Contains(m.DataType, "(") && strings.Contains(m.DataType, ")") {
		bef := strings.Index(m.DataType, "(")
		aft := strings.Index(m.DataType, ")")
		if precision, err = strconv.Atoi(m.DataType[bef+1 : aft]); err != nil {
			return ct
		}
	}
	ct.Type = "time"
	ct.FixedSwitch = true
	ct.GroupType = m.DataType
	ct.ShowSize = precision
	ct.DataLengthUnit = "byte"
	ct.Symbol = unsigned
	switch strings.ToUpper(m.ColumnType) {
	case "DATE":
		/*
			DATE 类型:
				存储日期,格式为 YYYY-MM-DD。
				取值范围从 1000-01-01 到 9999-12-31。
				占用 3 个字节的存储空间。
		*/
		ct.DataLength = 3
		ct.Format = "year-month-day"
	case "TIME":
		/*
			TIME 类型:
					存储时间,格式为 HH:MM:SS。
					取值范围从 -838:59:59 到 838:59:59。
					占用 3 个字节的存储空间。
				MySQL 中 TIME(n) 数据类型的 n 最大值是 6。也就是说,MySQL 支持的最大时间值小数精度为 6 位小数。
			具体解释如下:
				TIME(n) 数据类型用于存储时间值,其中 n 表示小数部分的位数。
					当 n 取值为 0 时,时间值精确到秒。
					当 n 取值为 1 到 6 时,时间值精确到小数点后 1 到 6 位。
					n 的最大值为 6，这意味着 TIME(6) 可以存储时间值精确到微秒(百万分之一秒)。
			例如:
					TIME(0) 存储为 'HH:MM:SS'
					TIME(3) 存储为 'HH:MM:SS.sss'
					TIME(6) 存储为 'HH:MM:SS.sssSss'
					需要注意的是,虽然 MySQL 支持 TIME(n) 最大 n 值为 6，但实际使用时根据需要选择合适的精度即可。过高的时间值精度可能会占用更多的存储空间,并且在某些情况下可能无法提供额外的价值。
		*/
		ct.DataLength = 3
		switch precision {
		case 0:
			ct.Format = "hour:minute:second"
		case 1:
			ct.Format = "hour:minute:second.milli(1)"
		case 2:
			ct.Format = "hour:minute:second.milli(2)"
		case 3:
			ct.Format = "hour:minute:second.milli(3)"
		case 4:
			ct.Format = "hour:minute:second.micro(1)"
		case 5:
			ct.Format = "hour:minute:second.micro(2)"
		case 6:
			ct.Format = "hour:minute:second.micro(3)"
		}
		ct.MaxSize = 6
	case "YEAR":
		/*
			YEAR 类型:
				存储年份,格式为 YYYY。
				取值范围从 1901 到 2155。
				占用 1 个字节的存储空间。
		*/
		ct.DataLength = 1
		ct.Format = "year"
	case "DATETIME":
		/*
			DATETIME 类型:
				存储日期和时间,格式为 YYYY-MM-DD HH:MM:SS。
				取值范围从 1000-01-01 00:00:00 到 9999-12-31 23:59:59。
				占用 8 个字节的存储空间。
		*/
		switch precision {
		case 0:
			ct.Format = "year-month-day hour:minute:second"
		case 1:
			ct.Format = "year-month-day hour:minute:second.milli(1)"
		case 2:
			ct.Format = "year-month-day hour:minute:second.milli(2)"
		case 3:
			ct.Format = "year-month-day hour:minute:second.milli(3)"
		case 4:
			ct.Format = "year-month-day hour:minute:second.micro(1)"
		case 5:
			ct.Format = "year-month-day hour:minute:second.micro(2)"
		case 6:
			ct.Format = "year-month-day hour:minute:second.micro(3)"
		}
		ct.MaxSize = 6
		ct.DataLength = 8
	case "TIMESTAMP":
		/*
			MySQL 中的 TIMESTAMP 数据类型是一种特殊的日期时间类型,具有以下特点:
			定义:
				TIMESTAMP 用于存储日期和时间信息,包括年、月、日、时、分、秒。时间戳的格式为 YYYY-MM-DD HH:MM:SS。
			范围:
				TIMESTAMP 的取值范围从 '1970-01-01 00:00:01' UTC 到 '2038-01-19 03:14:07' UTC。
			存储大小:
				TIMESTAMP 类型占用 4 个字节的存储空间。
			时区支持:
				TIMESTAMP 值是以 UTC 时区存储的。在插入或查询时,MySQL 会根据当前会话的时区自动进行转换。
			默认值:
				如果不指定 TIMESTAMP 列的默认值,则默认为当前时间戳。可以通过 DEFAULT CURRENT_TIMESTAMP 指定默认值为当前时间戳。
			自动更新:
				TIMESTAMP 列可以被定义为在行更新时自动更新为当前时间戳。通过 ON UPDATE CURRENT_TIMESTAMP 可以实现自动更新。
			与 DATETIME 的区别:
				DATETIME 是一种独立的日期时间类型,不受时区影响。TIMESTAMP 受时区影响,存储的是 UTC 时间,查询时会根据会话时区转换。
		*/
		ct.Type = "timestamp"
		switch precision {
		case 0:
			ct.Format = "year-month-day hour:minute:second.utc"
			ct.DataLength = 4
		case 1:
			ct.Format = "year-month-day hour:minute:second.milli(1).utc"
			ct.DataLength = 5
		case 2:
			ct.Format = "year-month-day hour:minute:second.milli(2).utc"
			ct.DataLength = 5
		case 3:
			ct.Format = "year-month-day hour:minute:second.milli(3).utc"
			ct.DataLength = 5
		case 4:
			ct.Format = "year-month-day hour:minute:second.micro(1).utc"
			ct.DataLength = 5
		case 5:
			ct.Format = "year-month-day hour:minute:second.micro(2).utc"
			ct.DataLength = 5
		case 6:
			ct.Format = "year-month-day hour:minute:second.micro(3).utc"
			ct.DataLength = 5
		}
	}
	return ct
}
func (cs TableColumn) charTypeRedefine(m global.TableMeta) global.ConvertColumnMeta {
	var (
		ct        global.ConvertColumnMeta
		precision int
		err       error
	)
	/*
		定义长度:
			CHAR 类型需要在定义时指定长度,最大长度为 255 个字符。
			例如, CHAR(10) 表示最多可以存储 10 个字符的字符串。
		存储空间:
			CHAR 类型会占用固定的存储空间,即定义的长度个字节。
			例如, CHAR(10) 类型会占用 10 个字节的存储空间。
		填充空格:
			如果存储的字符串长度小于定义的长度,CHAR 类型会用空格进行填充。
			例如, 存储 "hello" 到 CHAR(10) 类型,实际存储的值为 "hello "。
		比较和检索:
			在比较或检索 CHAR 类型时,会自动删除尾部的空格。所以 "hello " 和 "hello" 在比较时会被认为是相等的。
		性能:
			由于 CHAR 类型占用固定的存储空间,因此在检索和比较时性能较好。
			但如果大多数情况下存储的字符串长度小于定义的长度,会浪费存储空间。
		使用场景:
			CHAR 类型适合用于存储固定长度的数据,例如国家代码、邮政编码等。
			如果字符串长度不确定,建议使用 VARCHAR 类型。
	*/
	if strings.Contains(m.DataType, "(") && strings.Contains(m.DataType, ")") {
		bef := strings.Index(m.DataType, "(")
		aft := strings.Index(m.DataType, ")")
		if precision, err = strconv.Atoi(m.DataType[bef+1 : aft]); err != nil {
			return ct
		}
	}
	ct.Type = "char"
	ct.ShowSize = precision
	ct.FixedSwitch = true
	ct.GroupType = m.DataType
	ct.DataLengthUnit = "char"
	ct.DataLength = precision
	return ct
}
func (cs TableColumn) varcharTypeRedefine(m global.TableMeta) global.ConvertColumnMeta {
	var (
		ct        global.ConvertColumnMeta
		precision int
		err       error
	)
	/*
		定义长度:
			VARCHAR 类型需要在定义时指定最大长度,最大长度为 65,535 个字符。
			例如, VARCHAR(50) 表示最多可以存储 50 个字符的字符串。
		存储空间:
			VARCHAR 类型会根据实际存储的字符串长度来占用存储空间。存储空间为字符串长度加 1 或 2 个字节,具体取决于最大长度是否超过 255 个字符。
			例如, 存储 "hello" 到 VARCHAR(50) 类型,实际占用 6 个字节的存储空间。
		填充空格:
			与 CHAR 类型不同, VARCHAR 类型不会自动填充空格。存储的字符串长度有多长,就占用多少存储空间。
		比较和检索:
			在比较或检索 VARCHAR 类型时,会保留字符串中的空格。所以 "hello" 和 "hello " 在比较时会被认为是不相等的。
		性能:
			由于 VARCHAR 类型的存储空间是可变的,因此在某些情况下性能可能略低于 CHAR 类型。但相比之下, VARCHAR 类型更加灵活,能够更好地适应不确定长度的字符串。
		使用场景:
			VARCHAR 类型适合用于存储长度不确定的字符串数据,例如用户名、地址、备注等。如果字符串长度基本固定,建议使用 CHAR 类型以获得更好的性能。
	*/
	if strings.Contains(m.DataType, "(") && strings.Contains(m.DataType, ")") {
		bef := strings.Index(m.DataType, "(")
		aft := strings.Index(m.DataType, ")")
		if precision, err = strconv.Atoi(m.DataType[bef+1 : aft]); err != nil {
			return ct
		}
	}
	ct.Type = "varchar"
	ct.ShowSize = precision
	ct.FixedSwitch = true
	ct.GroupType = m.DataType
	ct.DataLengthUnit = "char"
	ct.DataLength = precision
	return ct
}
func (cs TableColumn) enumTypeRedefine(m global.TableMeta) global.ConvertColumnMeta {
	var (
		ct global.ConvertColumnMeta
	)
	ct.Type = "enum"
	ct.GroupType = m.DataType
	ct.DataLengthUnit = "char"
	ct.DataLength = 65535
	return ct
}
func (cs TableColumn) jsonTypeRedefine(m global.TableMeta) global.ConvertColumnMeta {
	var (
		ct global.ConvertColumnMeta
	)
	ct.Type = "json"
	ct.GroupType = m.DataType
	ct.DataLengthUnit = "char"
	ct.DataLength = 65535
	return ct
}
func (cs TableColumn) textTypeRedefine(m global.TableMeta) global.ConvertColumnMeta {
	var (
		ct global.ConvertColumnMeta
	)
	switch strings.ToUpper(m.ColumnType) {
	case "TINYTEXT":
		/*
			可变长文本类型
			最大长度为255个字符（2^8-1）。
			实际占用空间为字符长度 + 1-2 个字节
		*/
		ct.Type = "text"
		ct.DataLengthUnit = "char"
		ct.ShowSize = 255
		ct.DataLength = 1022
		ct.GroupType = m.DataType
	case "TEXT":
		/*
			可变长的大文本类型
			定义长度:
				TEXT 类型不需要在定义时指定长度,因为它可以存储最大长度为 65,535 个字符的文本。
				最大长度为65535个字符（2^16-1）
			存储空间:
				TEXT 类型会根据实际存储的文本长度来占用存储空间。实际占用的存储空间为文本长度加 2 个字节。最大可存储 65,535 个字节(约 64KB)的数据。
			填充空格:
				TEXT 类型不会自动填充空格,存储的文本长度有多长就占用多少存储空间。
			比较和检索:
				在比较或检索 TEXT 类型时,会保留文本中的所有空格和换行符。
			性能:
				由于 TEXT 类型的存储空间是可变的,因此在某些情况下性能可能略低于 CHAR 或 VARCHAR 类型。但相比之下, TEXT 类型能够存储更大量的文本数据。
			使用场景:
				TEXT 类型适合用于存储大文本内容,例如博客文章、产品描述、评论等。如果需要存储的文本长度不会超过 65,535 个字符,建议使用 TEXT 类型。
			相关类型:
				MySQL 还提供了 MEDIUMTEXT 和 LONGTEXT 类型,能够存储更大的文本内容。MEDIUMTEXT 最大可存储 16,777,215 个字节(约 16MB)的数据。LONGTEXT 最大可存储 4,294,967,295 个字节(约 4GB)的数据。
		*/
		ct.Type = "text"
		ct.DataLengthUnit = "char"
		ct.ShowSize = 65535
		ct.DataLength = 65535
		ct.GroupType = m.DataType
	case "MEDIUMTEXT":
		/*
			MySQL 中的 MEDIUMTEXT 数据类型是一种可变长度的大文本类型,它是 TEXT 类型的扩展版本。以下是关于 MEDIUMTEXT 类型的一些详细信息:
			定义长度:
				MEDIUMTEXT 类型不需要在定义时指定长度,因为它可以存储最大长度为 16,777,215 个字符的文本。
				最大长度为16777215个字符（2^24-1）
			存储空间:
				MEDIUMTEXT 类型会根据实际存储的文本长度来占用存储空间。实际占用的存储空间为文本长度加 3 个字节。最大可存储 16,777,215 个字节(约 16MB)的数据。
			填充空格:
				MEDIUMTEXT 类型不会自动填充空格,存储的文本长度有多长就占用多少存储空间。
			比较和检索:
				在比较或检索 MEDIUMTEXT 类型时,会保留文本中的所有空格和换行符。
			性能:
				由于 MEDIUMTEXT 类型的存储空间是可变的,因此在某些情况下性能可能略低于 CHAR 或 VARCHAR 类型。但相比之下, MEDIUMTEXT 类型能够存储更大量的文本数据。
			使用场景:
				MEDIUMTEXT 类型适合用于存储大文本内容,例如博客文章、产品描述、评论等。如果需要存储的文本长度在 65,535 个字符和 16,777,215 个字符之间,建议使用 MEDIUMTEXT 类型。
		*/
		ct.Type = "text"
		ct.DataLengthUnit = "char"
		ct.ShowSize = 65535
		ct.DataLength = 16777215
		ct.GroupType = m.DataType
	case "LONGTEXT":
		/*
			MySQL 中的 LONGTEXT 数据类型是一种可变长度的大文本类型,它是 TEXT 类型的最大版本。以下是关于 LONGTEXT 类型的一些详细信息:
			定义长度:
				LONGTEXT 类型不需要在定义时指定长度,因为它可以存储最大长度为 4,294,967,295 个字符的文本。
			存储空间:
				LONGTEXT 类型会根据实际存储的文本长度来占用存储空间。实际占用的存储空间为文本长度加 4 个字节。最大可存储 4,294,967,295 个字节(约 4GB)的数据。
			填充空格:
				LONGTEXT 类型不会自动填充空格,存储的文本长度有多长就占用多少存储空间。
			比较和检索:
				在比较或检索 LONGTEXT 类型时,会保留文本中的所有空格和换行符。
			性能:
				由于 LONGTEXT 类型的存储空间是可变的,因此在某些情况下性能可能略低于 CHAR 或 VARCHAR 类型。但相比之下, LONGTEXT 类型能够存储更大量的文本数据。
			使用场景:
				LONGTEXT 类型适合用于存储大量的大文本内容,例如文章、电子书、文档等。如果需要存储的文本长度超过 16,777,215 个字符,建议使用 LONGTEXT 类型。
			相关类型:
				MySQL 还提供了 TEXT 和 MEDIUMTEXT 类型,能够存储更小的文本内容。TEXT 最大可存储 65,535 个字节(约 64KB)的数据。MEDIUMTEXT 最大可存储 16,777,215 个字节(约 16MB)的数据。
			总的来说, LONGTEXT 类型是 MySQL 中用于存储大量大文本内容的数据类型,是 TEXT 类型的最大版本。它能够满足大多数需要存储大量文本数据的场景。对于需要存储较小文本内容的情况,可以使用 TEXT 或 MEDIUMTEXT 类型。
		*/
		ct.Type = "text"
		ct.DataLengthUnit = "char"
		ct.ShowSize = 16777215
		ct.DataLength = 4294967295
		ct.GroupType = m.DataType
	}
	return ct
}
func (cs TableColumn) lobTypeRedefine(m global.TableMeta) global.ConvertColumnMeta {
	var (
		ct        global.ConvertColumnMeta
		precision int
		err       error
	)
	if strings.Contains(m.DataType, "(") && strings.Contains(m.DataType, ")") {
		bef := strings.Index(m.DataType, "(")
		aft := strings.Index(m.DataType, ")")
		if precision, err = strconv.Atoi(m.DataType[bef+1 : aft]); err != nil {
			return ct
		}
	}
	ct.Type = "lob"
	ct.DataLengthUnit = "byte"
	switch strings.ToUpper(m.ColumnType) {
	case "BINARY":
		/*
			MySQL 中的 BINARY 数据类型是一种固定长度的二进制数据类型。以下是关于 BINARY 类型的一些详细信息:
			定义长度:
				BINARY 类型需要在定义时指定长度,长度范围为 1 到 255 个字节。
			存储空间:
				BINARY 类型占用的存储空间等于指定的长度。例如, BINARY(10) 类型会占用 10 个字节的存储空间。
			填充空格:
				当插入数据时,如果数据长度小于指定长度,BINARY 类型会用 0x00(null 字节)填充剩余空间。在检索数据时,会返回原始的填充字节。
			比较和检索:
				BINARY 类型的比较和检索都是基于二进制字节的逐个比较。比较时,会区分大小写和空格。
			性能:
				BINARY 类型的存储和检索性能比 CHAR 类型更好,因为它不需要字符集转换。
			使用场景:
				BINARY 类型适合用于存储二进制数据,如图片、音频、视频等。也可以用于存储定长的字符数据,如 GUID、MD5 等。
			相关类型:
				MySQL 还提供了 VARBINARY 类型,用于存储可变长度的二进制数据。VARBINARY 类型的最大长度为 65,535 个字节。
		*/
		ct.FixedSwitch = true
		ct.ShowSize = precision
		ct.DataLength = precision + 1
		ct.MaxSize = 255
		ct.GroupType = m.DataType
	case "VARBINARY":
		/*
			MySQL 中的 VARBINARY 数据类型是一种可变长度的二进制数据类型。以下是关于 VARBINARY 类型的一些详细信息:
			定义长度:
				VARBINARY 类型需要在定义时指定最大长度,长度范围为 1 到 65,535 个字节。
			存储空间:
				VARBINARY 类型会根据实际存储的二进制数据长度来占用存储空间。实际占用的存储空间为数据长度加 1 个字节。最大可存储 65,535 个字节(约 64KB)的数据。
			填充空格:
				VARBINARY 类型不会自动填充空格,存储的数据长度有多长就占用多少存储空间。
			比较和检索:
				在比较或检索 VARBINARY 类型时,会保留二进制数据的所有字节。
			性能:
				由于 VARBINARY 类型的存储空间是可变的,因此在某些情况下性能可能略低于 BINARY 类型。但相比之下, VARBINARY 类型能够存储更大量的二进制数据。
			使用场景:
				VARBINARY 类型适合用于存储可变长度的二进制数据,例如图片、音频、视频等。如果需要存储的二进制数据长度在 1 到 65,535 个字节之间,建议使用 VARBINARY 类型。
			相关类型:
				MySQL 还提供了 BINARY 类型,用于存储定长的二进制数据。
				BINARY 类型的最大长度为 255 个字节。
			总的来说, VARBINARY 类型是 MySQL 中用于存储可变长度二进制数据的数据类型。它能够满足大多数需要存储二进制数据的场景。对于需要存储定长二进制数据的情况,可以使用 BINARY 类型。
		*/

		ct.FixedSwitch = true
		ct.ShowSize = precision
		ct.DataLength = precision + 1
		ct.MaxSize = 65535
		ct.GroupType = m.DataType
	case "TINYBLOB":
		ct.DataLength = 255
		ct.MaxSize = 255
		ct.ShowSize = 255
		ct.GroupType = m.DataType
		ct.Format = "TINYBLOB"
	case "BLOB":
		ct.DataLength = 65535
		ct.MaxSize = 65535
		ct.ShowSize = 65535
		ct.GroupType = m.DataType
		ct.Format = "BLOB"
	case "MEDIUMBLOB":
		ct.DataLength = 16777215
		ct.MaxSize = 16777215
		ct.ShowSize = 16777215
		ct.GroupType = m.DataType
		ct.Format = "MEDIUMBLOB"
	case "LONGBLOB":
		ct.DataLength = 4294967295
		ct.MaxSize = 4294967295
		ct.ShowSize = 4294967295
		ct.GroupType = m.DataType
		ct.Format = "LONGBLOB"
	}
	return ct
}
func (cs TableColumn) gisTypeRedefine(m global.TableMeta) global.ConvertColumnMeta {
	var (
		ct global.ConvertColumnMeta
	)
	ct.Type = "gis"
	ct.GroupType = m.DataType
	ct.Format = "geometry"
	return ct
}

func (cs TableColumn) stringToInt(s string) (int, error) {
	if l, err := strconv.Atoi(s); err != nil {
		return 0, err
	} else {
		return l, nil
	}
}

// DefaultValueQuotation 处理默认值是否+引号问题，int类型或使用函数则不需要引号
func DefaultValueQuotation(m string) bool {
	for _, v := range []string{
		"TINYINT", "SMALLINT", "MEDIUMINT", "INT", "BIGINT", "FLOAT", "DOUBLE", "DECIMAL", "DEC",
	} {
		if strings.HasPrefix(strings.ToUpper(m), v) {
			return true
		}
	}
	return false
}
func (cs TableColumn) DefaultValueFunc(m global.TableMeta) string {
	switch {
	case strings.HasPrefix(strings.ToUpper(m.ColumnDefault), "CURRENT_TIMESTAMP"):
		return "year-month-day hour:minute:second.micro(3)"
	case strings.HasPrefix(strings.ToUpper(m.ColumnDefault), "CURRENT_DATE"), strings.HasPrefix(strings.ToUpper(m.ColumnDefault), "NOW()"):
		return "year-month-day hour:minute:second"
	case strings.HasPrefix(strings.ToUpper(m.ColumnDefault), "CURRENT_TIME"):
		return "hour:minute:second"
	case strings.HasPrefix(strings.ToUpper(m.ColumnDefault), "USER()"):
		return "current_user@host"
	case strings.HasPrefix(strings.ToUpper(m.ColumnDefault), "CURRENT_USER()"):
		return "current_user"
	case strings.HasSuffix(strings.ToUpper(m.ColumnDefault), "SYSTEM_USER()"):
		return "current_sys_user"
	case strings.HasPrefix(strings.ToUpper(m.ColumnDefault), "UUID()"):
		return "uuid"
	case strings.HasPrefix(strings.ToUpper(m.ColumnDefault), "SYS_GUID()"):
		return "sys_guid"
	case strings.HasPrefix(strings.ToUpper(m.ColumnDefault), "RAND()"):
		return "randValue"
	case strings.HasPrefix(strings.ToUpper(m.ColumnDefault), "CONNECTION_ID()"):
		return "current_id"
	default:
		return ""
	}
}
func (cs TableColumn) DefaultValue(m global.TableMeta) global.ConvertDefaultValue {
	var (
		ct global.ConvertDefaultValue
	)
	//处理字符串
	defaultValue := strings.ReplaceAll(m.ColumnDefault, "_utf8mb4", "")
	defaultValue = strings.ReplaceAll(defaultValue, "\\", "")
	if strings.EqualFold(defaultValue, "''") || len(defaultValue) == 0 {
		if DefaultValueQuotation(m.DataType) {
			if strings.EqualFold(m.IsNull, "NO") {
				ct.NewValue = ""
			}
		} else {
			if strings.EqualFold(m.IsNull, "NO") {
				ct.NewValue = "''"
			}
		}
		ct.NewValue = "NULL"
		return ct
	}
	//去除开头的''
	if strings.HasPrefix(defaultValue, "'") && strings.HasSuffix(defaultValue, "'") {
		defaultValue = defaultValue[1 : len(defaultValue)-1]
	}
	//判断default 是否用到函数
	ct.Format = cs.DefaultValueFunc(m)
	//判断数据类型是否是字符串，如果是则加上''
	//if !cs.DefaultValueQuotation(m) {
	//	if defaultValue != "''" { //处理默认值不是('')的
	//		defaultValue = fmt.Sprintf("'%v'", defaultValue)
	//	}
	//}
	ct.NewValue = defaultValue
	return ct
}

// ColumnTypeRedefine 根据相应规范，重新定义数据类型
func (cs TableColumn) ColumnTypeRedefine(m global.TableMeta) global.TableMeta {
	var (
		event = "[ColumnTypeRedefine]"
		ccm   global.ConvertColumnMeta
		cdv   global.ConvertDefaultValue
		n     = m
	)
	switch strings.ToUpper(m.ColumnType) {
	case "BIT", "TINYINT", "SMALLINT", "MEDIUMINT", "INT", "BIGINT": //整数数值类型
		ccm = cs.intTypeRedefine(m)
	case "FLOAT", "DOUBLE": //单精度和双精度的浮点数类型
		ccm = cs.FloatTypeRedefine(m)
	case "DECIMAL": //定长数值类型
		ccm = cs.DecimalTypeRedefine(m)
	case "DATE", "TIME", "YEAR", "DATETIME", "TIMESTAMP": //时间类型
		ccm = cs.timeTypeRedefine(m)
	case "CHAR": //字符串定长
		ccm = cs.charTypeRedefine(m)
	case "VARCHAR": //字符串变长
		ccm = cs.varcharTypeRedefine(m)
	case "JSON":
		ccm = cs.jsonTypeRedefine(m)
	case "ENUM":
		ccm = cs.enumTypeRedefine(m)
	case "TINYTEXT", "TEXT", "MEDIUMTEXT", "LONGTEXT": //大文本类型
		ccm = cs.textTypeRedefine(m)
	case "BINARY", "VARBINARY", "TINYBLOB", "BLOB", "MEDIUMBLOB", "LONGBLOB": //二进制类型
		ccm = cs.lobTypeRedefine(m)
	case "POINT", "GEOMETRY":
		ccm = cs.gisTypeRedefine(m)
	default:
		WLog.Warn(fmt.Sprintf("(%v) %v The data type %v of column name %v of current table %v.%v does not match, the conversion cannot be performed, and the default value is returned.", 1, event, m.ColumnType, m.ColumnName, m.DatabaseName, m.TableName))
	}
	cdv = cs.DefaultValue(m)
	n.ConvertColumnType = ccm
	n.ColumnType = ccm.GroupType
	n.ConvertDefaultValue = cdv
	return n
}
